/**
 * This file contains things shared by multiple .xul files
 * 
 * @author Tatiana Kichkaylo
 */
/** XUL namespace for element generation */
const
XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
/** URL of our server */
const
SERVER_URL = "http://tabbinder.appspot.com";
// SERVER_URL = "http://localhost:8080";
/** Binder methods URL */
const
BINDER_URL = SERVER_URL + "/masterbind/binder";
/** Key for attaching data to check boxes, menu items, etc */
const
DATA_KEY = "mydata";
/** Id of the tree element in the side bar */
const
BINDER_TREE_ID = "thebindertree";
/** Login URL */
const
LOGIN_URL = SERVER_URL + "/_ah/login?continue=%2Fmasterbind%2Fuser";
/** String for global storage */
const
STORAGE_NS = "firebinder";
/** URL for Google accounts */
const
GOOGLE_ACCTS = "https://www.google.com/accounts/NewAccount?service=mail";

/** Poor woman's reflection */
function inspect(obj, withValues) {
	var msg = '';
	for ( var p in obj) {
		if (withValues)
			msg = msg + ", " + p + "=" + obj[p];
		else
			msg = msg + ", " + p;
	}
	return msg;
}

function inspectList(list) {
	var res = list.length + " ";
	for ( var i = 0; i < list.length; i++)
		res = res + "\n" + list.item(i);
	return res;
}

/** Open the URL in a new tab */
function openPage(url) {
	var tab = window.top.getBrowser().addTab(url);
	window.top.getBrowser().selectedTab = tab;
}

/** Remove all children of the given element */
function clearChildren(element) {
	while (element.hasChildNodes()) {
		element.removeChild(element.firstChild);
	}
}

/**
 * Make sure we are logged in, then call okCall. Pop up dialog if necessary. If
 * login fails on server, show alert. If user cancels the dialog, call failCall.
 * failCall may be set to false.
 * 
 * parent is the parent window used to open the dialog from.
 * 
 * All calls are synchronous, the dialog is modal.
 * 
 * Note: XMLHttpRequest::open takes (method,url,[isAsynch=true],[user],[pass])
 */
function withLogin(okCall, failCall, parent) {
	// Already logged in?
	var auth = getAuth();
	// alert("winLogin " + auth + "\n" + okCall + ", " + failCall + ", " +
	// parent);
	if (auth && auth != "false") {
		// alert("Going right in " + auth);
		try {
			okCall();
			return;
		} catch (e) {
			// alert(e);
		}
	}
	// Either no auth at all or an exception. Pass browser as argument
	// so that we can add a tab for redirection to Google accounts
	parent.openDialog('chrome://firebinder/content/LoginDialog.xul',
			'LoginWin', 'chrome, modal', function a() {
				fetchCookie(okCall)
			}, failCall, parent); // , parent.top.getBrowser()
}

/** Callback from the login dialog. Get this if got a good Auth string */
function fetchCookie(okCall) {
	var xmlhttp = new XMLHttpRequest();
	var longUrl = LOGIN_URL + "&auth=" + getAuth();

	xmlhttp.open("GET", longUrl, false);
	try {
		// If cannot get to the server, will throw
		xmlhttp.send(null);
		// alert("After #1 " + xmlhttp.status + ": " + xmlhttp.responseText);
		if (xmlhttp.status == 401) {
			return false;
		} else if (xmlhttp.status == 200) {
			// responseText has username\nemail\nlogout url
			// parseLoginResponse(xmlhttp.responseText);
			okCall();
			return true;
		} else {
			// This must be a server error
			alert("Server problem #" + xmlhttp.status + ": "
					+ xmlhttp.responseText);
		}
	} catch (e) {
		alert("Cannot connect to the server " + SERVER_URL);
	}
	// if (failCall)
	// failCall();
	return false;
}

/** Use with actual Google server */
function doLogin(user, passwd, okCall, failCall) {
	// alert("doLogin " + user + ", " + passwd);
	var params = "accountType=HOSTED_OR_GOOGLE&Email=" + user + "&Passwd="
			+ passwd + "&service=ah&source=tabbinder.appspot.com";
	var xmlhttp = new XMLHttpRequest();
	// last param false => sync, default true => async
	xmlhttp.open("POST", "https://www.google.com/accounts/ClientLogin");
	xmlhttp.setRequestHeader("Content-type",
			"application/x-www-form-urlencoded");
	xmlhttp.setRequestHeader("Content-length", params.length);
	// readyState: UNSENT = 0; OPENED = 1; HEADERS_RECEIVED = 2;
	// LOADING = 3; DONE = 4;
	xmlhttp.onreadystatechange = function() { 
		if (xmlhttp.readyState != 4)
			return;
		if (xmlhttp.status == 403) {
			alert("Login failed");
			if (failCall)
				setTimeout(failCall, 0);
		} else if (xmlhttp.status == 200) {
			// Pick Auth from the response
			var parts = xmlhttp.responseText.split("\n");
			for ( var i = 0; i < parts.length; i++)
				if (parts[i].indexOf("Auth=") == 0) {
					setAuth(parts[i].substr(5));
				}
			okCall();
		} else {
			// This must be a server error
			alert("Server problem #" + xmlhttp.status + ": " + xmlhttp.responseText);
			if (failCall)
				setTimeout(failCall, 0);
		}
	}
	// xmlhttp.setRequestHeader("Connection", "close");
	xmlhttp.send(params);
	// alert("After call " + xmlhttp.status + ": " + xmlhttp.responseText);
	
	// close the dialog, there is nothing we can do here
	return true;
}

/** Binder call with a message and a callback, which can be false/undefined */
function binderCall(message, method, callback) {
	var xmlhttp = new XMLHttpRequest();
	// Hack around DELETE being ignored by Google server
	if (method == "DELETE")
		xmlhttp.open("POST", BINDER_URL + "?action=delete", false);
	else
		xmlhttp.open(method, BINDER_URL, false);
	try {
		// If cannot get to the server, will throw
		xmlhttp.send(message);
		if (xmlhttp.status == 200) {
			// var resp = xmlhttp.responseText;
			if (!callback)
				alert("Done");
			else
				callback();
		} else {
			// The call is wrapped in login, so this must be a server error
			alert("Server problem #" + xmlhttp.status + ": "
					+ xmlhttp.responseText);
		}
	} catch (e) {
		alert("Cannot connect to the server " + SERVER_URL);
	}
}

/** Send the new or updated binder (in json) to the server */
function sendBinder(binder, callback) {
	binderCall(JSON.stringify(binder), "POST", callback);
}

function deleteBinder(id, callback) {
	binderCall(id, "DELETE", callback);
}

/** Helper function for adding an element to the given container */
function addNewElement(container, type, text, data) {
	var item = document.createElementNS(XUL_NS, type);
	item.setAttribute("label", text);
	item.setAttribute(DATA_KEY, data);
	container.appendChild(item);
	return item;
}

function getBinders(callback, id) {
	var xmlhttp = new XMLHttpRequest();
	if (!id)
		xmlhttp.open("GET", BINDER_URL, true);
	else
		xmlhttp.open("GET", BINDER_URL + "?id=" + id, true);
	xmlhttp.onreadystatechange = function() { 
		if (xmlhttp.readyState != 4)
			return;
		if (xmlhttp.status == 200) {
			var json = xmlhttp.responseText;
			// alert("Got JSON " + json);
			// Get array of binder objects
			callback(JSON.parse(json));
		} else {
			// The call is wrapped in login, so this must be a server error
			// alert("Server problem #" + xmlhttp.status + ": "
			// + xmlhttp.responseText);
			setAuth(false);
			// throw "Stale auth in getBinders";
			callback(null);
		}
	}
	try {
		// If cannot get to the server, will throw
		xmlhttp.send(null);
	} catch (e) {
		alert("Cannot connect to the server " + SERVER_URL);
		callback(null);
	}
}

/**
 * Put all tab info into a string list - 2 items per tab. Needs to happen here
 * rather than in the dialog, because the dialog does not see the correct window
 */
function getTabsAsList() {
	var browser = window.top.getBrowser().tabContainer;
	// alert("Tab count " + browser.itemCount);

	var tabs = [];
	for ( var i = 0; i < browser.itemCount; i++) {
		var item = browser.getItemAtIndex(i).linkedBrowser;
		tabs[i * 2] = item.contentTitle;
		tabs[i * 2 + 1] = item.contentDocument.location;
	}
	return tabs;
}

/**
 * Find last tab index for the given url. tabs is the list returned by
 * getTabsAsList
 */
function findTab(tabs, url) {
	for ( var i = tabs.length - 1; i > 0; i = i - 2)
		if (tabs[i] == url)
			return (i - 1) / 2;
	return -1;
}

/** Close binder. Takes Json binder object */
function closeBinder(binder) {
	var browser = window.top.getBrowser();
	var tabs = getTabsAsList();
	for ( var i = 0; i < binder.links.length; i++) {
		var last = findTab(tabs, binder.links[i].url);
		if (last >= 0) {
			browser.removeTab(browser.mTabs[last]);
			tabs = getTabsAsList();
		}
	}
}

function openCreateBinderBox(callback) {
	window.openDialog('chrome://firebinder/content/dialog.xul', 'createbinder',
			'chrome', getTabsAsList(), callback);
}

function getAuth() {
	return window.top.globalStorage[STORAGE_NS].auth;
}

function setAuth(value) {
	window.top.globalStorage[STORAGE_NS].auth = value;
}

function gotoGoogle(browser) {
	// Last element is the dialog itself. Earlier elements are CSS refs
	var buts = document.childNodes[document.childNodes.length - 1];
	buts.cancelDialog();

	// Use the passed reference to browser, since window.top from a dialog
	// points to something that does not have getBrowser() method
	var tab = browser.addTab(GOOGLE_ACCTS);
	browser.selectedTab = tab;
}
